package org.zhuzx.util.pool;

import java.util.HashMap;

/**
 * 不可变的身份标识Map，与IdentityHashMap类似，通过==而非equals匹配键。<br>
 * 但与IdentityHashMap不同的是，不依赖于键的hashCode方法，避免了某些特殊哈希函数引发的性能问题乃至错误。<br>
 * 某些类的不同实例会得到相同哈希值，比如相同模式的多个SimpleDateFormat。<br>
 * 更重要的是，还有部分特殊的对象会动态生成哈希值，比如ByteBuffer会根据当前存储的内容实时计算。
 * @author	zhuzx	2017年11月7日
 */
public class ImmutableIdentityMap<K,V> {
	
	private ImmutableIntMap<Entry<K,V>> nativeHashCodeMap;
	
	private static class Entry<K,V> {
		K key;
		V value;
		Entry<K,V> next;
		public Entry(K key, V value, Entry<K, V> next) {
			this.key = key;
			this.value = value;
			this.next = next;
		}
	}
	
	public ImmutableIdentityMap(K[] keys, V[] values) {
		/*
		 * 通常每个存活对象都有唯一的nativeHashCode，但依赖于虚拟机实现。更何况以无限多的对象映射为有限的int值，本质上会存在重复。
		 * http://hllvm.group.iteye.com/group/topic/39183
		 * http://www.cnblogs.com/godtrue/p/6395098.html
		 */
		HashMap<Integer,Entry<K,V>> codeMap = new HashMap<>(keys.length);
		for (int i = 0; i < keys.length; i++) {
			K key = keys[i];
			if (key == null) throw new IllegalArgumentException("不支持空键");
			Integer code = System.identityHashCode(key);
			codeMap.put(code, new Entry<K,V>(key, values[i], codeMap.get(code)));
		}
		nativeHashCodeMap = new ImmutableIntMap<>(codeMap);
	}
	
	public V get(K key) {
		if (key == null) return null;
		Entry<K,V> e = nativeHashCodeMap.get(System.identityHashCode(key));
		for (; e != null; e = e.next) {
			if (e.key == key) {
				return e.value;
			}
		}
		return null;
	}
	
}